04 文本分析器 - awk
- AWK 由两位贝尔实验室的计算机科学家 Alfred Aho 和 Peter Weinberger 以及著名的计算机科学家 Brian Kernighan 共同开发。
- 其命名取自三位发明人姓的首字母 Aho、Weinberger 和 Kernighan。
awk是一种命令行工具,用于处理和操作文本文件。awk用于模式扫描和处理,它能够对文本文件进行复杂的文本分析和处理。awk通过提供编程语言的功能,如变量、数学运算、字符串处理等,使得对文本文件的分析和操作变得非常灵活和高效。
详细教程:Linux awk 命令 | 菜鸟教程
基本语法
bash
awk 'pattern {action}' fileoptions:选项,用于控制awk的行为。pattern:一个定义了哪些行会被处理的正则表达式或条件表达式。action:一组在匹配到 pattern 的行上执行的命令,封闭在大括号{}中。file:待处理的文件名。
TIP
如果没有指定 pattern,则 action 会应用于所有行。如果没有指定 action,则默认的行为是打印每个匹配的行。
Options 参数
| 选项 | 描述 |
|---|---|
-F <分隔符> 或 --field-separator=<分隔符> | 指定输入字段的分隔符,默认是空格。使用这个选项可以指定不同于默认分隔符的字段分隔符。 |
-v <变量名>=<值> | 设置 awk 内部的变量值。可以使用该选项将外部值传递给 awk 脚本中的变量。 |
-f <脚本文件> | 指定一个包含 awk 脚本的文件。这样可以在文件中编写较大的 awk 脚本,然后通过 -f 选项将其加载。 |
-V 或 --version | 显示 awk 的版本信息。 |
-h 或 --help | 显示 awk 的帮助信息,包括选项和用法示例。 |
自定义变量
-v varname=value,变量名区分字符大小写。- 在
action中直接定义。
基本概念
模式 - 动作语法
AWK 的基本结构是 pattern { action },即对于每一个符合模式的输入行,执行相应的动作。模式和动作部分都是可选的。
- 模式:可以是正则表达式、关系表达式、组合表达式等。
- 动作:由一系列的 AWK 语句组成,用于处理匹配的行。
字段和记录
- 记录:默认情况下,AWK 将每一行视为一个记录。
- 字段:每条记录可以分为多个字段,默认以空白(空格或制表符)分隔字段。字段使用
$1,$2, ...$n表示,$0表示整个记录。
主要特性
- 模式匹配:
awk使用扩展的正则表达式来匹配文本行,从而进行特定的处理。 - 内置变量和操作符:提供了丰富的内置变量(如
NR表示当前行号,NF表示当前行的字段数)和操作符,便于处理文本和数值数据。 - 字段处理能力:
awk默认将每行文本分割成多个字段,字段之间默认由空白字符分隔。用户可以轻松访问和操作这些字段。 - 编程元素:提供了完整的编程语言功能,包括变量、条件语句、循环控制、数组、函数等。
- 文本和数值函数:内置许多用于操作文本字符串和数值的函数,如
substr(),length(),int(),sin(), 等等。
关键特性
字段和记录分隔符:
- 默认情况下,
awk将字段(变量$1,$2, …)视为由空格或制表符分隔的单词。 - 可以使用
-F选项或FS内置变量来指定字段分隔符。 - 记录分隔符默认是换行符,可以使用
RS内置变量来更改。
- 默认情况下,
内置变量:
变量 描述 FS(Field Separator)输入字段分隔符,默认为空白字符(空格或制表符)。 OFS(Output Field Separator)输出字段分隔符,默认为空白字符(空格或制表符)。 RS(Record Separator)输入记录分隔符,默认为换行符( \n),指定输入时的换行符。ORS(Output Record Separator)输出记录分隔符,默认为换行符( \n),输出时用指定符号代替换行符。NF(Number of Fields)当前行的字段个数,即当前行被分割成了几列。 NR(Number of Records)行号,表示当前处理的文本行的总行号。 FNR(File Number of Records)表示各文件分别计数的行号。 ARGC(Argument Count)命令行参数的个数。 ARGV(Argument Vector)数组,保存的是命令行所给定的各参数。 $n当前记录的第 n个字段,字段间由FS分隔。$0完整的输入记录。 ARGIND(Argument Index)命令行中当前文件的位置 (从 0 开始算)。 CONVFMT(Conversion Format)数字转换格式 (默认值为 %.6g)。ENVIRON(Environment)环境变量关联数组。 ERRNO(Error Number)最后一个系统错误的描述。 FIELDWIDTHS(Field Widths)字段宽度列表 (用空格键分隔)。 FILENAME(Filename)当前文件名。 IGNORECASE(Ignore Case)如果为真,则进行忽略大小写的匹配。 OFMT(Output Format)数字的输出格式 (默认值是 %.6g)。RLENGTH(Match Length)由 match函数所匹配的字符串的长度。RSTART(Match Start)由 match函数所匹配的字符串的第一个位置。SUBSEP(Subscript Separator)数组下标分隔符 (默认值是 \034)。模式匹配:
- 可以使用正则表达式作为模式。
/pattern/:模式匹配。! /pattern/:模式不匹配。
条件语句:
if、else if、else。
循环:
for、while、do-while。
示例
简单示例
bash
Name Age City
Alice 25 New York
Bob 30 Los Angeles
Charlie 35 Chicagobash
name,age,city
Alice,25,New York
Bob,30,Los Angeles
Charlie,35,Chicago
David,28,San Francisco打印所有行
bash➜ ~ awk '{print}' data.txt Name Age City Alice 25 New York Bob 30 Los Angeles Charlie 35 Chicago打印特定字段
bash➜ ~ awk '{print $1, $3}' data.txt # 只打印每行的姓名和城市 Name City Alice New Bob Los Charlie Chicago条件打印
bash➜ ~ awk '$2 > 30 {print $1, $2}' data.txt # 只打印年龄大于 30 的记录 Name Age Charlie 35 ➜ ~ awk '/Los|New/ {print}' data.txt # 打印包含 Los 或者 New 的行 Alice 25 New York Bob 30 Los Angeles ➜ ~ awk '{ if ($2 > 30) print $1 " ("$2 ") is older than 30"; else print $1 " ("$2 ") is not older than 30"; }' data.txt # 根据年龄是否大于 30 来打印不同的消息 Name (Age) is older than 30 Alice (25) is not older than 30 Bob (30) is not older than 30 Charlie (35) is older than 30 ➜ ~ cat data.csv | awk -F, '$2 ~ /^[0-9]+$/{print $1,$2}' | awk 'max < $2 {max = $2} END {print max}' # 打印 data.csv 文件中 age 中的最大值 35使用内置变量
bash➜ ~ awk '{print NR, $0}' data.txt # 打印每行的行号和该行的内容 1 Name Age City 2 Alice 25 New York 3 Bob 30 Los Angeles 4 Charlie 35 Chicago统计文件的行数
bash➜ ~ awk 'END {print NR}' data.txt 4文本加工
bash➜ ~ awk '{temp = $1; $1 = $2; $2 = temp; print}' data.txt # 将每行的第一个字段和第二个字段的值交换 Age Name City 25 Alice New York 30 Bob Los Angeles 35 Charlie Chicago ➜ ~ awk '{sum += $2} END {print sum}' data.txt # 对第二字段进行求和 90 ➜ ~ awk -F, 'BEGIN {OFS="\t"} NR==1 {print $0} NR>1 {$2=$2+5; print $0}' data.csv # 对 data.csv 中的 age 字段 +5 name,age,city Alice 35 New York Bob 30 Los Angeles Charlie 40 Chicago David 33 San Francisco ➜ ~ awk -F, -va=5 'BEGIN {OFS="\t"} NR==1 {print $0} NR>1 {$2=$2+a; print $0}' data.csv name,age,city Alice 35 New York Bob 30 Los Angeles Charlie 40 Chicago David 33 San Francisco修改字段分隔符(例如,如果输入文件用逗号分隔)
bash➜ ~ awk -F, '{print $1, $2}' data.csv name age Alice 25 Bob 30 Charlie 35 David 28 ➜ ~ awk -F, '$2 ~ /^[0-9]+$/{print $1,$2}' data.csv # 排除第一行 Alice 25 Bob 30 Charlie 35 David 28
常用示例
bash
➜ ~ top -n 2| grep %Cpu | tail -n 1 | awk -F "," '{print $4}'
99.4 idtop -n 2: 运行top命令,-n 2参数表示只显示两次top的输出结果,然后退出。这个命令通常用于查看系统资源的使用情况,如 CPU、内存等。| grep %Cpu: 使用管道符|将top的输出传递给grep命令,用于过滤包含%Cpu的行。%Cpu是top输出中用于显示 CPU 使用率的部分。| tail -n 1: 使用管道将grep的输出传递给tail命令,-n 1参数表示只取最后一行。这是因为top命令的输出中可能会包含多行%Cpu的信息,我们只需要最后一行。| awk -F "," '{print $4}': 最后,使用管道将tail的输出传递给awk命令。-F ","参数指定awk使用逗号作为字段分隔符,'{print $4}'则表示打印第四个字段。
综合起来,这条命令的作用是从 top 命令的输出中获取当前 CPU 使用率的信息。具体来说,它提取了 %Cpu 行中第四个字段的值,通常这个字段包含的是 CPU 的使用率百分比。
bash
➜ ~ vim lol.info
➜ ~ cat lol.info
name: 亚索
city: 诺克萨斯
name: 盖伦
city: 德玛西亚
name: 蔚
city: 皮尔特沃夫
name: 提莫
city: 艾欧尼亚
name: 凯南
city: 班德尔城
name: 琴女
city: 诺克萨斯
➜ ~ cat lol.info | awk '/^name/{name=$0;next;} {print name " - " $0 }'
name: 亚索 - city: 诺克萨斯
name: 盖伦 - city: 德玛西亚
name: 蔚 - city: 皮尔特沃夫
name: 提莫 - city: 艾欧尼亚
name: 凯南 - city: 班德尔城
name: 琴女 - city: 诺克萨斯
➜ ~ awk 'BEGIN {FS=": "; OFS=" - "} /^name/ {name = $2} /^city/ {print "name: " name OFS "city: " $2}' lol.info
name: 亚索 - city: 诺克萨斯
name: 盖伦 - city: 德玛西亚
name: 蔚 - city: 皮尔特沃夫
name: 提莫 - city: 艾欧尼亚
name: 凯南 - city: 班德尔城
name: 琴女 - city: 诺克萨斯/^name/ {name = $0; next}: 当遇到以"name"开头的行时,将整行内容赋给变量name,然后跳过当前循环(next)。{print name " - " $0}: 对于其他行(即不以"name"开头的行),打印之前保存的name变量和当前行内容的组合。
BEGIN {FS=": "; OFS=" - "}: 在处理开始前,设置字段分隔符FS为": ",设置输出字段分隔符OFS为" - "。这样设置之后,输入的每一行会按照": "分割字段,输出时会用" - "连接字段。/^name/ {name = $2}: 当遇到以"name"开头的行时,将第二个字段的值赋给变量name。/^city/ {print "name: " name OFS "city: " $2}: 当遇到以"city"开头的行时,打印输出"name: " name OFS "city: " $2。这里name是之前保存的英雄姓名,$2是当前行的城市信息。
整体模式
bash
awk 'BEGIN{commands} pattern{commands} END{commands}'BEGIN{commands}-只会执行一次,常用于变量初始化,打印表头等信息pattern{commands}- 以行为单位处理的END{commands}-只会执行一次,常用于统计结果打印
bash
➜ ~ cat data.csv
name,age,city
Alice,30,New York
Bob,25,Los Angeles
Charlie,35,Chicago
David,28,San Francisco
➜ ~ cat data.csv | head -1 | awk 'BEGIN{ print "BEGIN" } { print $0 } END{ print "END" }'
BEGIN
name,age,city
END
➜ ~ cat data.csv |tail -1 | awk 'BEGIN{ print "BEGIN" } { print $0 } END{ print "END" }'
BEGIN
David,28,San Francisco
END示例:将 top 命令输出中前六个进程的 PID、%CPU、%MEM 和 COMMAND 信息输出
bash
➜ ~ top -n 1 -b | awk 'NR > 6 && NR <= 13 {print $1, $9, $10, $12}' OFS=" - " # 以 " - " 分隔
PID - %CPU - %MEM - COMMAND
1584097 - 6.7 - 0.1 - top
1 - 0.0 - 0.3 - systemd
2 - 0.0 - 0.0 - kthreadd
3 - 0.0 - 0.0 - pool_workqueu+
4 - 0.0 - 0.0 - kworker/R-rcu+
5 - 0.0 - 0.0 - kworker/R-rcu+
➜ ~ top -n 1 -b | awk 'NR > 6 && NR <= 13 {printf "%-8s - %-5s - %-5s - %s\n", $1, $9, $10, $12}'
PID - %CPU - %MEM - COMMAND
1 - 0.0 - 0.3 - systemd
2 - 0.0 - 0.0 - kthreadd
3 - 0.0 - 0.0 - pool_workqueu+
4 - 0.0 - 0.0 - kworker/R-rcu+
5 - 0.0 - 0.0 - kworker/R-rcu+
6 - 0.0 - 0.0 - kworker/R-slu+
➜ ~ top -n 1 -b | awk 'NR > 6 && NR <= 13 {printf "%8s - %5s - %5s - %s\n", $1, $9, $10, $12}'
PID - %CPU - %MEM - COMMAND
1 - 0.0 - 0.3 - systemd
2 - 0.0 - 0.0 - kthreadd
3 - 0.0 - 0.0 - pool_workqueu+
4 - 0.0 - 0.0 - kworker/R-rcu+
5 - 0.0 - 0.0 - kworker/R-rcu+
6 - 0.0 - 0.0 - kworker/R-slu+
➜ ~ top -n 1 -b | awk 'NR > 6 && NR <= 13 {print $1 "\t" $9 "\t" $10 "\t" $12}'
PID %CPU %MEM COMMAND
1 0.0 0.3 systemd
2 0.0 0.0 kthreadd
3 0.0 0.0 pool_workqueu+
4 0.0 0.0 kworker/R-rcu+
5 0.0 0.0 kworker/R-rcu+
6 0.0 0.0 kworker/R-slu+
➜ ~ top -n 1 -b | awk 'NR > 6 && NR <= 13 {print $1, $9, $10, $12}' OFS="\t"
PID %CPU %MEM COMMAND
1 0.0 0.3 systemd
2 0.0 0.0 kthreadd
3 0.0 0.0 pool_workqueu+
4 0.0 0.0 kworker/R-rcu+
5 0.0 0.0 kworker/R-rcu+
6 0.0 0.0 kworker/R-slu+top -n 1 -b: 运行top命令,-n 1表示只运行一次,-b表示以批处理模式运行,输出结果不会被交互式控制台影响。awk 'NR > 6 && NR <= 13': 使用 awk 处理top命令的输出,NR > 6 && NR <= 13表示处理行号大于 6 且小于等于 13 的行(即舍弃前 6 行,然后处理接下来的七行)。printf "%-8s - %-5s - %-5s - %s\n":使用printf函数来格式化输出。%-8s表示左对齐且占用 8 个字符的字符串格式。这样,第一列(PID)、第二列(%CPU)、第三列(%MEM)和第四列(COMMAND)的宽度都会是 8 个字符,保证了对齐性。$1, $9, $10, $12:这些是 awk 命令中指定的列,分别对应 PID、%CPU、%MEM 和 COMMAND。
示例:监控并计算在 6 秒内系统 CPU 时间 (sys 时间) 的平均值。
bash
➜ ~ top -n 7 -d 1 -b | grep '%Cpu' | tail -6 | awk '{print; sum += $4; printf "第 %d 秒的 sys 时间:%.2f\n", NR, $4} END {printf "\n6 秒钟 top 信息 sys 时间的平均值:%.2f\n", sum / NR}'
%Cpu(s): 0.2 us, 0.7 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 1 秒的 sys 时间:0.70
%Cpu(s): 0.2 us, 0.7 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 2 秒的 sys 时间:0.70
%Cpu(s): 0.5 us, 1.5 sy, 0.0 ni, 97.0 id, 0.7 wa, 0.0 hi, 0.2 si, 0.0 st
第 3 秒的 sys 时间:1.50
%Cpu(s): 0.2 us, 0.5 sy, 0.0 ni, 97.2 id, 2.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 4 秒的 sys 时间:0.50
%Cpu(s): 0.2 us, 0.5 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 5 秒的 sys 时间:0.50
%Cpu(s): 0.2 us, 0.5 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 6 秒的 sys 时间:0.50
6 秒钟 top 信息 sys 时间的平均值:0.73
➜ ~ top -n 7 -d 1 -b | grep '%Cpu' | tail -6 | awk '
{
print
sum += $4
printf "第 %d 秒的 sys 时间:%.2f\n", NR, $4
}
END {
printf "\n6 秒钟 top 信息 sys 时间的平均值:%.2f\n", sum / NR
}
'
%Cpu(s): 0.0 us, 0.5 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 1 秒的 sys 时间:0.50
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 2 秒的 sys 时间:0.20
%Cpu(s): 0.2 us, 0.7 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 3 秒的 sys 时间:0.70
%Cpu(s): 0.5 us, 0.7 sy, 0.0 ni, 97.5 id, 1.2 wa, 0.0 hi, 0.0 si, 0.0 st
第 4 秒的 sys 时间:0.70
%Cpu(s): 0.0 us, 0.5 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 5 秒的 sys 时间:0.50
%Cpu(s): 0.5 us, 0.5 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
第 6 秒的 sys 时间:0.50
6 秒钟 top 信息 sys 时间的平均值:0.52top -n 7 -d 1 -b:top命令提供正在运行的进程的实时视图。-n 7表示top将更新 7 次。因为第一次更新通常是初始数据采集,通常不计入计算,所以剩下的 6 次更新被用于实际的 CPU 时间监控。-d 1设置每次更新之间的延迟为 1 秒。-b让top以批处理模式运行,适合脚本解析输出。
grep '%Cpu':过滤top输出,只保留包含 CPU 使用信息的行。tail -6:选择过滤后的输出的最后 6 行,这些行对应于 6 秒内的 CPU 使用情况。awk '{print; sum += $4; printf "第 %d 秒的 sys 时间:%.2f\n", NR, $4} END {printf "\n6 秒钟 top 信息 sys 时间的平均值:%.2f\n", sum / NR}':print;:打印原始行。sum += $4;:将第 4 列(对应于sysCPU 时间)累加到sum中。printf "第 %d 秒的 sys 时间:%.2f\n", NR, $4;:用中文打印每秒的sys时间。- 在
END块中,处理完所有行后,打印 6 秒内sysCPU 时间的平均值。